/*******************************************************************************
* Copyright (c) 2005, 2009 committers of openArchitectureWare and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* committers of openArchitectureWare - initial API and implementation
* QNX Software Systems - No lookup of EDataTypeTypes in getType(Object)
*******************************************************************************/
package org.eclipse.xtend.typesystem.emf;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.Enumerator;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.EEnum;
import org.eclipse.emf.ecore.EEnumLiteral;
import org.eclipse.emf.ecore.EGenericType;
import org.eclipse.emf.ecore.ENamedElement;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.ETypedElement;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.mwe.utils.SingleGlobalResourceSet;
import org.eclipse.internal.xtend.expression.parser.SyntaxConstants;
import org.eclipse.internal.xtend.type.baseimpl.BuiltinMetaModel;
import org.eclipse.internal.xtend.util.Cache;
import org.eclipse.xtend.expression.TypeSystem;
import org.eclipse.xtend.type.impl.java.JavaBeansMetaModel;
import org.eclipse.xtend.typesystem.MetaModel;
import org.eclipse.xtend.typesystem.Type;
public class EmfRegistryMetaModel implements MetaModel {
private final Log log = LogFactory.getLog(getClass());
TypeSystem typeSystem;
private EObjectType eobjectType = null;
// Used to resolve Java based datatypes.
private JavaBeansMetaModel internalJbmm = new JavaBeansMetaModel();
public EObjectType getEobjectType() {
return eobjectType;
}
private final Cache<EObject, Type> cache = new Cache<EObject, Type>() {
@Override
protected Type createNew(EObject param) {
if (param == null) {
return null;
}
if (param instanceof EGenericType) {
EGenericType genericType = (EGenericType) param;
if (typeSystem != null) {
Type innerType = getTypeForEClassifier(genericType.getETypeArguments().get(0));
return new EmfListType(innerType, typeSystem, BuiltinMetaModel.LIST);
}
else {
param = genericType.getEClassifier();
}
}
if (!(param instanceof EClassifier))
return null;
EClassifier ele = (EClassifier) param;
if (ele.getName() == null) {
return null;
}
if (ele instanceof EClass) {
return new EClassType(EmfRegistryMetaModel.this, getFullyQualifiedName(ele), (EClass) ele);
}
else if (ele instanceof EEnum) {
return new EEnumType(EmfRegistryMetaModel.this, getFullyQualifiedName(ele), (EEnum) ele);
}
else if (ele instanceof EDataType) {
if (typeSystem != null) {
if (stringTypes.contains(ele)) {
return typeSystem.getStringType();
}
else if (booleanTypes.contains(ele)) {
return typeSystem.getBooleanType();
}
else if (intTypes.contains(ele)) {
return typeSystem.getIntegerType();
}
else if (realTypes.contains(ele)) {
return typeSystem.getRealType();
}
else if (objectTypes.contains(ele)) {
return typeSystem.getObjectType();
}
else if (listTypes.contains(ele)) {
return new EmfListType(typeSystem.getObjectType(), typeSystem, BuiltinMetaModel.LIST);
}
}
EDataType dataType = (EDataType) ele;
if (dataType.getInstanceClassName()!=null) {
// fall back to qualified Java name for EDatatypes
Type t = internalJbmm.getTypeForName(dataType.getInstanceClassName().replace(".", "::"));
if (t == null) {
// for primitives
t = typeSystem.getTypeForName(dataType.getInstanceClassName().replace(".", "::"));
}
return t;
} else {
return typeSystem.getObjectType();
}
}
return null;
}
};
private final HashSet<EClassifier> stringTypes;
private final HashSet<EClassifier> intTypes;
private final HashSet<EClassifier> realTypes;
private final HashSet<EClassifier> booleanTypes;
private final HashSet<EClassifier> objectTypes;
private final HashSet<EClassifier> listTypes;
public void setUseSingleGlobalResourceSet(boolean b) {
if (b) {
addResourceSet(SingleGlobalResourceSet.get());
}
}
public void setTypeSystem(final TypeSystem typeSystem) {
this.typeSystem = typeSystem;
this.internalJbmm.setTypeSystem(typeSystem);
if (eobjectType == null) {
eobjectType = new EObjectType(getTypeSystem());
}
}
public EmfRegistryMetaModel() {
stringTypes = new HashSet<EClassifier>();
stringTypes.add(EcorePackage.eINSTANCE.getEString());
stringTypes.add(EcorePackage.eINSTANCE.getEChar());
stringTypes.add(EcorePackage.eINSTANCE.getECharacterObject());
booleanTypes = new HashSet<EClassifier>();
booleanTypes.add(EcorePackage.eINSTANCE.getEBoolean());
booleanTypes.add(EcorePackage.eINSTANCE.getEBooleanObject());
intTypes = new HashSet<EClassifier>();
intTypes.add(EcorePackage.eINSTANCE.getEInt());
intTypes.add(EcorePackage.eINSTANCE.getEIntegerObject());
intTypes.add(EcorePackage.eINSTANCE.getELong());
intTypes.add(EcorePackage.eINSTANCE.getELongObject());
intTypes.add(EcorePackage.eINSTANCE.getEShort());
intTypes.add(EcorePackage.eINSTANCE.getEShortObject());
intTypes.add(EcorePackage.eINSTANCE.getEByte());
intTypes.add(EcorePackage.eINSTANCE.getEByteObject());
intTypes.add(EcorePackage.eINSTANCE.getEBigInteger());
realTypes = new HashSet<EClassifier>();
realTypes.add(EcorePackage.eINSTANCE.getEFloat());
realTypes.add(EcorePackage.eINSTANCE.getEFloatObject());
realTypes.add(EcorePackage.eINSTANCE.getEDouble());
realTypes.add(EcorePackage.eINSTANCE.getEDoubleObject());
realTypes.add(EcorePackage.eINSTANCE.getEBigDecimal());
objectTypes = new HashSet<EClassifier>();
objectTypes.add(EcorePackage.eINSTANCE.getEJavaObject());
listTypes = new HashSet<EClassifier>();
listTypes.add(EcorePackage.eINSTANCE.getEEList());
}
private final Cache<String, Type> typeForNameCache = new Cache<String, Type>() {
@Override
protected Type createNew(String typeName) {
if (typeName.equals(eobjectType.getName())) {
return eobjectType;
}
Set<ENamedElement> ele = getNamedElementRec(allPackages(), typeName);
if (ele.size() > 1) {
boolean classifiers = true;
for (ENamedElement element : ele) {
classifiers = classifiers && element instanceof EClassifier;
}
if (classifiers) {
log.warn("Multiple types (" + ele.size() + ") with name " + typeName + " found!");
}
}
if (ele.isEmpty()) {
return null;
}
ENamedElement type = ele.iterator().next();
if (type instanceof EClassifier) {
return cache.get(type);
}
return null;
}
};
private EPackage[] _allPackages;
/**
* returns the managed packages. Uses the global EPackage Registry.
*
* @return
*/
protected EPackage[] allPackages() {
if (_allPackages != null) {
return _allPackages;
}
Set<EPackage> packages = new HashSet<EPackage>();
for (String name : new HashSet<String>(EPackage.Registry.INSTANCE.keySet())) {
try {
packages.add(EPackage.Registry.INSTANCE.getEPackage(name));
}
catch (Exception e) {
log.error(e, e);
}
}
for (ResourceSet rs : resourceSets) {
for (String name : rs.getPackageRegistry().keySet()) {
try {
packages.add(rs.getPackageRegistry().getEPackage(name));
}
catch (Exception e) {
log.error(e, e);
}
}
}
_allPackages = packages.toArray(new EPackage[packages.size()]);
return _allPackages;
}
private final Set<ResourceSet> resourceSets = new HashSet<ResourceSet>();
public void addResourceSet(ResourceSet rs) {
this.resourceSets.add(rs);
}
public Type getTypeForName(final String typeName) {
return typeForNameCache.get(typeName);
}
private Set<ENamedElement> getNamedElementRec(final ENamedElement[] elements, final String name) {
Set<ENamedElement> result = new HashSet<ENamedElement>();
final String[] frags = name.split(SyntaxConstants.NS_DELIM);
final String firstFrag = frags[0];
for (final ENamedElement ele : elements) {
final String eleName = getElementName(ele);
if (eleName != null && eleName.equals(firstFrag)) {
if (frags.length > 1) {
final Collection<ENamedElement> children = EcoreUtil.getObjectsByType(ele.eContents(),
EcorePackage.eINSTANCE.getENamedElement());
result.addAll(getNamedElementRec(children.toArray(new ENamedElement[children.size()]), name
.substring(name.indexOf(SyntaxConstants.NS_DELIM) + SyntaxConstants.NS_DELIM.length())));
}
else {
result.add(ele);
}
}
}
return result;
}
public Type getType(final Object obj) {
if (obj instanceof EObject) {
if (obj instanceof EEnumLiteral && ((EEnumLiteral) obj).getName() != null
&& ((EEnumLiteral) obj).getEEnum() != null) {
final EEnumLiteral el = (EEnumLiteral) obj;
return getTypeForEClassifier(el.getEEnum());
}
final EClass clazz = ((EObject) obj).eClass();
return getTypeForEClassifier(clazz);
}
final Set<Type> types = getKnownTypes();
if (obj instanceof Enumerator) {
for (Type t : types) {
// check for EEnumType because the EDataTypeType for
// ecore::EEnumerator matches all enumerator instances
if (t instanceof EEnumType) {
if (t.isInstance(obj)) {
return t;
}
}
}
}
return null;
}
private Set<Type> knownTypes = null;
public Set<Type> getKnownTypes() {
if (knownTypes == null) {
final Set<Type> result = new HashSet<Type>();
result.add(eobjectType);
List<EObject> waiting = new LinkedList<EObject>();
waiting.addAll(Arrays.asList(allPackages()));
while (!waiting.isEmpty()) {
EObject elem = waiting.remove(0);
if (elem instanceof EPackage) {
EPackage pkg = (EPackage) elem;
waiting.addAll(pkg.getESubpackages());
waiting.addAll(pkg.getEClassifiers());
}
else if (elem instanceof EClassifier) {
EClassifier cls = (EClassifier) elem;
try {
Type t = getTypeForEClassifier(cls);
if (t != null)
result.add(t);
}
catch (RuntimeException e) {
log.error(e.getMessage(),e);
}
}
}
knownTypes = result;
return result;
}
return knownTypes;
}
public Type getTypeForEClassifier(final EClassifier element) {
if (element == null) {
return getTypeSystem().getVoidType();
}
return cache.get(element);
}
public Type getTypeForEClassifier(final EGenericType element) {
EClassifier baseType = null;
if (element.getEClassifier() != null) {
baseType = element.getEClassifier();
} else {
baseType = element.getERawType();
}
if ((baseType == null) || !listTypes.contains(baseType))
return getTypeForEClassifier(baseType);
if (element.getETypeArguments().size() != 1)
throw new RuntimeException("Unexpected number of type arguments");
return cache.get(element);
}
public Type getTypeForETypedElement(final ETypedElement typedElement) {
if (typedElement.getEType() == null) {
return getTypeSystem().getVoidType();
}
Type t = null;
if (typedElement.getEType() instanceof EDataType) {
if (typedElement.getEGenericType() != null) {
t = getTypeForEClassifier(typedElement.getEGenericType());
} else {
t = getTypeForEClassifier(typedElement.getEType());
}
}
else {
if (typedElement.getEGenericType() != null) {
t = getTypeForEClassifier(typedElement.getEGenericType());
} else {
t = getTypeSystem().getTypeForName(getFullyQualifiedName(typedElement.getEType()));
}
}
if (typedElement.isMany())
return new EmfListType(t, typeSystem, BuiltinMetaModel.LIST);
else
return t;
}
public TypeSystem getTypeSystem() {
return typeSystem;
}
public String getFullyQualifiedName(final ENamedElement ele) {
return getFqnRec(ele.eContainer(), getElementName(ele));
}
protected String getFqnRec(final EObject ele, final String suffix) {
if (ele == null || !(ele instanceof ENamedElement)) {
return suffix;
}
else {
return getFqnRec(ele.eContainer(), getElementName((ENamedElement) ele) + SyntaxConstants.NS_DELIM + suffix);
}
}
private void traversePackage(final String parentNamespace, final Set<String> namespaces, final EPackage thePackage) {
StringBuilder sb = new StringBuilder();
if (parentNamespace != null) {
sb.append(parentNamespace);
sb.append("::");
}
sb.append(getElementName(thePackage));
String namespace = sb.toString();
namespaces.add(namespace);
EList<EPackage> subpackages = thePackage.getESubpackages();
for (EPackage subPackage : subpackages) {
traversePackage(namespace, namespaces, subPackage);
}
}
public Set<String> getNamespaces() {
Set<String> result = new HashSet<String>();
EPackage[] allPackages = allPackages();
for (EPackage thePackage : allPackages) {
traversePackage(null, result, thePackage);
}
return result;
}
protected String getElementName(ENamedElement ele) {
return ele == null ? null : ele.getName();
}
}